<?php

declare(strict_types=1);

namespace app\command;

use think\console\Command;
use think\console\Input;
use think\console\Output;
use PhpMqtt\Client\MqttClient;
use PhpMqtt\Client\ConnectionSettings;
use app\common\controller\Mqtt as CommonMqtt;

class MQTT extends Command
{
    private static $mqtt;
    protected function configure()
    {
        // 指令配置
        $this->setName('mqtt')
            ->setDescription('the mqtt command');
    }

    protected function execute(Input $input, Output $output)
    {
        $connectionSettings  = new ConnectionSettings();
        $connect = $connectionSettings
            ->setUsername(env('mqtt.username'))
            ->setPassword(env('mqtt.password'))
            // 连接超时定义了客户端尝试与代理建立套接字连接的最大秒数。 
            // 该值不能小于 1 秒。
            ->setConnectTimeout(3)
            // 套接字超时是套接字连接的最大空闲时间（以秒为单位）。 
            // 如果在给定的秒数内没有读取或发送数据，则套接字将关闭。 
            // 该值不能小于 1 秒。
            ->setSocketTimeout(5)
            // 重新发送超时是客户端在发送未确认消息的副本之前等待的秒数。 
            // 该值不能小于 1 秒。
            ->setResendTimeout(10)
            // 保持活动间隔是客户端在向代理发送保持活动信号（ping）之前将等待而不
            //发送消息的秒数。 该值不能小于 1 秒，也不能大于 65535 秒。 一个合理
            //的值是 10 秒（默认值）。
            ->setKeepAliveInterval(10)
            // 如果客户端突然断开连接时代理应该以客户端的名义发布最后的遗嘱消息，
            // 则此设置定义将发布消息的主题。 仅当同时配置了此设置和最后遗嘱消息
            // 时，才会发布最后遗嘱消息。
            ->setLastWillTopic(null)
            // 如果客户端突然断开连接时代理应该以客户端的名义发布最后的遗嘱消息，
            // 此设置定义将要发布的消息。 仅当配置了此设置以及最后遗嘱主题时，
            // 才会发布遗嘱消息。
            ->setLastWillMessage(null)
            // 如果被触发，客户端的最后一条消息将被发布的服务质量级别。
            ->setLastWillQualityOfService(0)
            // 此标志确定是否保留客户端的最后一条消息（如果它被触发）。 使用此设置
            // 可以方便地通过在最后遗嘱中发布保留的离线状态和在线状态作为连接时的
            // 第一条消息来发出客户端离线的信号。
            ->setRetainLastWill(false)
            // 此标志确定是否应将 TLS 用于连接。 用于连接代理的端口必须支持 TLS 
            // 连接。
            ->setUseTls(false)
            // 此标志确定是否应验证对等方证书。 如果 TLS 用于连接，则此设置将强制
            // This flag determines if the peer certificate is verified, if TLS is used.
            ->setTlsVerifyPeer(true)
            // 此标志确定是否验证了对等方名称，是否使用了 TLS。
            ->setTlsVerifyPeerName(true)
            // 此标志确定是否应接受对等方的自签名证书。 将此设置为 TRUE 意味着存在
            // 安全风险，应避免用于生产场景和公共服务。
            ->setTlsSelfSignedAllowed(false)
            // 如果使用 TLS，则用于验证对等证书的证书颁发机构证书的路径。
            ->setTlsCertificateAuthorityFile(null)
            // 如果使用 TLS，则包含用于验证对等证书的证书颁发机构证书的目录的路径。
            ->setTlsCertificateAuthorityPath(null)
            // 用于身份验证的客户端证书文件的路径（如果使用 TLS）。客户端证书必须
            // 是 PEM 编码的。 它可以选择包含颁发者的证书链。
            ->setTlsClientCertificateFile(null)
            // 用于身份验证的客户端证书密钥文件的路径（如果使用 TLS）。 此选项还需
            // 要使用 ConnectionSettings::setTlsClientCertificateFile()。
            ->setTlsClientCertificateKeyFile(null)
            // 用于解密客户端证书私钥的密码短语，如果使用 TLS，则反过来用于身份验
            // 证。 此选项还需要使用ConnectionSettings::setTlsClientCertificateFile() 
            // 和 ConnectionSettings::setTlsClientCertificateKeyFile()。
            ->setTlsClientCertificateKeyPassphrase(null);
        // 用于身份验证的客户端证书密钥文件的路径（如果使用 TLS）。 此选项还需
        self::$mqtt = new MqttClient(env('mqtt.host'), (int)env('mqtt.port'), env('mqtt.clientid'));
        // 连接代理
        self::$mqtt->connect($connect, true);
        printf("client connected\n");
        // 订阅主题
        self::$mqtt->subscribe(env('mqtt.sub'), function ($topic, $message) {
            // 在这里处理接收到的 MQTT 消息
            $mq = new CommonMqtt();
            $mq->analysis($message);
        }, 1);
        self::$mqtt->loop();
    }
}
